HDU 5784 How Many Triangles(极角排序)

题意:

$给定N\le 2000个二维平面不重点,求形成的锐角三角形的个数$

分析:

$总而言之,这种就是极角排序之后然后two pointers$
$由于是个环倍增一下,无论你atan2搞还是点积叉积搞,思路都是一样的$
$来看看这题的两种计算姿势,一种是题解的比较tricky一点:$
$锐角三角形={cnt_{锐角}-2\times(cnt_{直角、钝角})\over 3},注意这里是角$
$至于怎么算的,看贡献,锐角三角形贡献3个锐角,直角、钝角三角形贡献2个锐角$
$这个统计方法也比较simple,two pointers先统计锐角、然后再统计锐、直、钝一起$
$一减就得到了想要的2个东西$
$对于第二种做法,比较general一点:$
$锐角三角形=总三角形数-直角、钝角、平角三角形数,注意这里是三角形$
$two pointers统计的时候直接把锐角和0°一起统计了$
$直角、钝角、平角三角形数=C(n-1, 2)-统计出来的$
$最后再用C(n, 3)减一下就好了$
$两者时间复杂度都是O(n^2logn)$
$注意atan2的精度,毕竟atan2(2e9, 1)在$ 1e-10 $的量级,多取2个量级取$ 1e-12 就好

代码一:

//
//  Created by TaoSama on 2016-08-02
//  Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cout << #x << " = " << x << "  "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
const double EPS = 1e-12, PI = acos(-1);

int sgn(double x) {
    return x < -EPS ? -1 : x > EPS;
}

int n;
struct Point {
    int x, y;
    double ang;
    void read() {scanf("%d%d", &x, &y);}
} p[N];

int calc(vector<Point>& v, double delta) {
    int angle = 0;
    for(int i = 0, j = 0, k = 0; i < n - 1; i = k + 1) {
        //collinear
        while(k + 1 < n - 1 && sgn(v[k + 1].ang - v[i].ang) == 0) ++k;
        j = max(j, k);
        while(j < v.size() && sgn(v[j].ang - v[i].ang - delta) < 0) ++j;
        angle += (k - i + 1) * (j - k - 1);
    }
    return angle;
}

int main() {
#ifdef LOCAL
    freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
//  freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    while(scanf("%d", &n) == 1) {
        for(int i = 1; i <= n; ++i) p[i].read();

        int acute = 0, obtuse = 0;
        for(int i = 1; i <= n; ++i) {
            vector<Point> v;
            for(int j = 1; j <= n; ++j) if(j != i) v.push_back(p[j]);
            for(int j = 0; j < n - 1; ++j)
                v[j].ang = atan2(v[j].y - p[i].y, v[j].x - p[i].x);
            sort(v.begin(), v.end(), [&](Point x, Point y) {
                return x.ang < y.ang;
            });

            //double it
            for(int j = 0; j < n - 1; ++j){
                Point tmp = v[j];
                tmp.ang += 2 * PI;
                v.push_back(tmp);
            }

            int curAcute = calc(v, PI / 2);
            int tot = calc(v, PI);

            acute += curAcute;
            obtuse += tot - curAcute;
        }
        int ans = (acute - 2 * obtuse) / 3;
        printf("%d\n", ans);
    }
    return 0;
}

代码二:
(来自mathon)

/************************************************
 *Author        :mathon
 *Email         :luoxinchen96@gmail.com
*************************************************/
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <stack>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
typedef unsigned long long ull;
#define xx first
#define yy second
#define pr(x) cout << #x << " " << x << " "
#define prln(x) cout << #x << " " << x << endl
template<class T> inline T lowbit(T x) { return x & (-x); }
const int MAXN = 2000 + 5;

struct Point {
    ll x, y;
    Point() {}
    Point(ll x, ll y): x(x), y(y) {}
    void read() {
        scanf("%lld%lld", &x, &y);
    }
    Point operator - (const Point& b) const {
        return Point(x - b.x, y - b.y);
    }
    ll cross(const Point& b) const {
        return x * b.y - y * b.x;
    }
    ll dot(const Point& b) const {
        return x * b.x + y * b.y;
    }
    void print() {
        printf("x = %lld, y = %lld\n", x, y);
    }
} ps[MAXN];
int n;

bool cmp(const Point& a, const Point& b) {
    if(a.y * b.y <= 0) {
        if(a.y > 0 || b.y > 0) return a.y < b.y;
        if(a.y == 0 && b.y == 0) return a.x < b.x;
    }
    return a.cross(b) > 0;
}

Point buf[MAXN * 2];

int main(void) {
#ifdef MATHON
    freopen("1004.in", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
    while(scanf("%d", &n) == 1) {
        for(int i = 0; i < n; i++) {
            ps[i].read();
        }
        ll ans = 0;
        for(int k = 0; k < n; k++) {
            int cnt = 0;
            for(int j = 0; j < n; j++) {
                if(k == j) continue;
                buf[cnt++] = ps[j] - ps[k];
            }
            sort(buf, buf + cnt, cmp);
            memcpy(buf + cnt, buf, sizeof(Point) * cnt);
            ll tmp = 0;
            for(int i = 0, j = 0; i < cnt; i++) {
                if(i == j) while(j < i + cnt && buf[i].cross(buf[j]) == 0
                                     && buf[i].dot(buf[j]) > 0) j++;
                while(j < i + cnt && buf[i].cross(buf[j]) > 0 && buf[i].dot(buf[j]) > 0) j++;
                tmp += j - i - 1;
                // pr(i); prln(j);
            }
            // prln(tmp);
            tmp = (cnt) * (cnt - 1) / 2  - tmp;
            ans += tmp;
        }
        ans = ll(n) * (n - 1) * (n - 2) / 6 - ans;
        cout << ans << endl;
    }
    return 0;
}